package ch.gislb2;

import ch.gislb2.exceptions.IntegratorException;
import ch.gislb2.interf.Integrator;

/**
* The Integrator is used to silence the noisy sensors. It is programmed to
* integrate over any-amount sampleSets. If the difference of the amplitude of
* the integration and newest sample is beyond a certain threshold, the
* integration is discarded. (usually this situation occurs if some real change
* is made to the sensor device.
*
* In this implementation a 'Ring-Array' is used to continuously add and remove
* data without having to (de-)allocate memory.
*
* Complexity: Average calculation: O(1) Add / Remove sample: O(1) Clear
* Ring-Array: O(1)
*
* @author SimpliX
*
*/
public class DigitalStabilizer  {
	private final double[] buffer; 
	   private int first;
	   private int last;
	   private int actualBufferSize = 0;
	   private double average;
	   private double sum;
	   private double maxNoiseError;
	   private double lastValue;
	   

	   //1/3sekunde
	   public DigitalStabilizer(final int bufferSize, final double maxNoiseError) throws IntegratorException {
	       if (bufferSize < 1) {
	           throw new IntegratorException("BufferSize too small");
	       }
	       this.buffer = new double[bufferSize];
	       this.setMaxNoiseError(maxNoiseError);
	   }

	   /**
	    * @see ch.simplix.android.zero.integrator.implementation.Integrator#setMaxNoiseError(int)
	    */
	   public synchronized void setMaxNoiseError(final double maxNoiseError) throws IntegratorException {
	       if (maxNoiseError < 0) {
	           throw new IntegratorException("Error cannot be smaller than 0");
	       }
	       this.maxNoiseError = maxNoiseError;
	   }

	   /**
	    * @see ch.simplix.android.zero.integrator.implementation.Integrator#addSample(int)
	    */
	   public synchronized void addSample(final double value) {
	       if (Math.abs(Math.abs(value) - Math.abs(this.getAverage())) > this.maxNoiseError) {
	           this.clear();
	       }
	       this.lastValue = this.buffer[this.last];
	       this.buffer[this.last] = value;
	       this.sum += value;
	       this.last = (this.last + 1) % this.buffer.length;
	       if (this.first != this.last) {
	           this.actualBufferSize++;
	       } else {
	           this.first = (this.last + 1) % this.buffer.length;
	           this.sum -= this.lastValue;
	       }

	   }

	   /**
	    * @see ch.simplix.android.zero.integrator.implementation.Integrator#clear()
	    */
	   public synchronized void clear() {
	       this.first = this.last;
	       this.actualBufferSize = 0;
	       this.sum = 0;
	       this.average = 0;
	   }

	   /**
	    * @see ch.simplix.android.zero.integrator.implementation.Integrator#getAverage()
	    */
	   public synchronized double getAverage() {
	       if (this.actualBufferSize > 0) {
	           this.average = (double) ((double)this.sum / (double)this.actualBufferSize);
	           //System.out.println("Average: " + this.average + "/sum: "+ this.sum + "/Buffer: "+this.actualBufferSize);
	       }
	       return this.average;
	   }
	   public synchronized double getRoundedAverage()
	   {
		   return (double)Math.round(average*10)/10;
	   }
	   public synchronized int getActualBufferSize()
	   {
		   return this.actualBufferSize;
	   }
}
